﻿using GCommon;
using LumenWorks.Framework.IO.Csv;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
//using TsiU;
using UnityEngine;
using System.Reflection;

namespace GCommon
{
    public class ConfigDataManager : SingletonModule<ConfigDataManager>
    {
        public bool UnLoadNoCacheConfigText;
        private Dictionary<string, List<CSVBaseData>> m_ConfigDataLists = new Dictionary<string, List<CSVBaseData>>();
        private Dictionary<string, Dictionary<string,CSVBaseData>> m_ConfigDataItemss = new Dictionary<string, Dictionary<string,CSVBaseData>>();
        private Dictionary<string, string> m_ConfigLoadResID = new Dictionary<string, string>();

        protected override void OnInit()
        {
        }
        protected override void OnCleanup()
        {
            m_ConfigDataLists = new Dictionary<string, List<CSVBaseData>>();
            m_ConfigDataItemss = new Dictionary<string, Dictionary<string,CSVBaseData>>();
            m_ConfigLoadResID = new Dictionary<string, string>();
        }
        
        public string LoadCsvText(string path)
        {
            UnityEngine.Object resourceObj = Resources.Load("Config/" + path); //ResourceManager.instance.GetResourceNoCache(resID);
            if(resourceObj == null)
            {
                //EndSample();
                return null;
            }
            
            string csvText = "";
            //TextAsset csvTextAsset = UnityEngine.Object.Instantiate(resourceObj) as TextAsset;
            TextAsset csvTextAsset = resourceObj as TextAsset;
            byte[] csv_bs;

#if ENABLE_ENCRYPTION
            ResDecryption.Decryption(csvTextAsset.bytes, out csv_bs);
#else
            csv_bs = csvTextAsset.bytes;
#endif
            csvText = Encoding.UTF8.GetString(csv_bs);
            
            if (UnLoadNoCacheConfigText)
                Resources.UnloadAsset(resourceObj);
            return csvText;
        }

        //public List<T> LoadCSVNoCache<T>(ResourceID resID) where T : CSVBaseData, new()
        //{
        //    var csvText = LoadCsvText(resID);
        //    if (string.IsNullOrEmpty(csvText))
        //    {
        //        return null;
        //    }

        //    var t = ParseCSV<T>(csvText);
        //    return t;
        //}

        public void LoadCSVNoCache<T, P>(string resID, ICollection<P> result) where T : P, new() where P : CSVBaseData
        {
            var csvText = LoadCsvText(resID);
            if (string.IsNullOrEmpty(csvText))
            {
                return;
            }

            ParseCSV<T, P>(csvText, result);
        }

        public void LoadCSVNoCache<T>(string resID, IDictionary<uint, T> result) where T : CSVBaseData, IGetId, new()
        {
            var csvText = LoadCsvText(resID);
            if (string.IsNullOrEmpty(csvText))
            {
                return;
            }
            ParseCSV<T>(csvText, result);
        }
        
        public void LoadCSV<T>(string resID) where T : CSVBaseData, new()
        {
            string typeKey = GetDataListsKey<T>();
            
            if (m_ConfigDataLists.ContainsKey(typeKey))
            {
                Debug.LogWarning("duplicated key in config: " + typeKey);
                return;
            }
            
            List<CSVBaseData> result = new List<CSVBaseData>();
            LoadCSVNoCache<T, CSVBaseData>(resID, result);
            string typeStr = typeof(T).ToString();
            
            for (int i = 0; i < result.Count; i++)
            {
                CSVBaseData item = result[i];
                string pKey = item.GetPrimaryKey();
                if(string.IsNullOrEmpty(pKey) == false)
                {
                    Dictionary<string, CSVBaseData> tmpDic;
                    //string itemKey = GetDataItemKey<T>(pKey);
                    if(!m_ConfigDataItemss.TryGetValue(typeStr, out tmpDic))
                    {
                        tmpDic = new Dictionary<string, CSVBaseData>();
                        m_ConfigDataItemss.Add(typeStr, tmpDic);
                    }
                    if (tmpDic.ContainsKey(pKey) == false)
                    {
                        tmpDic.Add(pKey, item);
                    }
                    else
                    {
                        Debug.LogWarning("duplicate item key: " + pKey + " | type :" + typeStr);
                    }
                }
            }
            m_ConfigDataLists.Add(typeKey, result);

            if (!m_ConfigLoadResID.ContainsKey(typeKey))
            {
                m_ConfigLoadResID.Add(typeKey, resID);
            }
        }
        
        public List<CSVBaseData> GetDataList<T>() where T : CSVBaseData
        {
            string typeKey = GetDataListsKey<T>();
            List<CSVBaseData> result;
            if(m_ConfigDataLists.TryGetValue(typeKey, out result))
            {
                return result;
            }
            return null;
        }
        public T GetData<T>(string key) where T : CSVBaseData
        {
            Dictionary<string, CSVBaseData> tmpDic;
            CSVBaseData result;
            if (m_ConfigDataItemss.TryGetValue(typeof(T).ToString(), out tmpDic))
            {
                if (tmpDic.TryGetValue(key, out result))
                {
                    return result.As<T>();
                }
            }
            return null;
        }
        private string GetDataListsKey<T>() where T : CSVBaseData
        {
            return typeof(T).ToString();
        }
        private string GetDataItemKey<T>(string pKey) where T : CSVBaseData
        {
            return string.Format("{0}|{1}", GetDataListsKey<T>(), pKey);
        }
        public static List<T> ParseCSV<T>(string csvText) where T : CSVBaseData, new()
        {
            var result = new List<T>();
            ParseCSV<T, T>(csvText, result);
            return result;
        }
        
        public static void ParseCSV<T, P>(string csvText, ICollection<P> result) where T : P, new() where P: CSVBaseData
        {
            TextReader reader = new StringReader(csvText);
            CsvReader csv = new CsvReader(reader, true);
            
            int fieldCount = csv.FieldCount;
            string[] headers = csv.GetFieldHeaders();
            csv.ReadNextRecord();//skip desc
            
            T test = (T)Activator.CreateInstance(typeof(T));
            if (test.UsingIndexedParseData)
            {
                var headCols = test.GetHeadColNames();
                var length = headCols.Length;
                var index = new int[length];
                for (int i = 0; i < headCols.Length; i++)
                {
                    var col = headCols[i];
                    for (var j = 0; j < headers.Length; j++)
                    {
                        if (col == headers[j])
                        {
                            index[i] = j;
                            break;
                        }
                    }
                }
                
                while (csv.ReadNextRecord())
                {
                    var obj = new T();
                    obj.ParseData(csv, index);
                    result.Add(obj);
                }
            }
            else
            {
                string[] values = new string[fieldCount];
                while (csv.ReadNextRecord())
                {
                    long index = csv.CurrentRecordIndex;
                    csv.CopyCurrentRecordTo(values);
                    var obj = new T();
                    obj.ParseData(index, fieldCount, headers, values);
                    result.Add(obj);
                }
            }
        }
        
        public static void ParseCSV<T>(string csvText, IDictionary<uint, T> result) where T : CSVBaseData, IGetId, new()
        {
            TextReader reader = new StringReader(csvText);
            CsvReader csv = new CsvReader(reader, true);
            
            int fieldCount = csv.FieldCount;
            string[] headers = csv.GetFieldHeaders();
            csv.ReadNextRecord();//skip desc
            
            var test = new T();
            if (test.UsingIndexedParseData)
            {
                var headCols = test.GetHeadColNames();
                var length = headCols.Length;
                var index = new int[length];
                for (int i = 0; i < headCols.Length; i++)
                {
                    var col = headCols[i];
                    for (var j = 0; j < headers.Length; j++)
                    {
                        if (col == headers[j])
                        {
                            index[i] = j;
                            break;
                        }
                    }
                }
                
                while (csv.ReadNextRecord())
                {
                    var obj = new T();
                    obj.ParseData(csv, index);
#if UNITY_EDITOR
                    result.Add(obj.GetId(), obj);
#else
                    result[obj.GetId()] = obj;
#endif
                }
            }
            else
            {
                string[] values = new string[fieldCount];
                while (csv.ReadNextRecord())
                {
                    long index = csv.CurrentRecordIndex;
                    csv.CopyCurrentRecordTo(values);
                    var obj = new T();
                    obj.ParseData(index, fieldCount, headers, values);
#if UNITY_EDITOR
                    result.Add(obj.GetId(), obj);
#else
                    result[obj.GetId()] = obj;
#endif
                }
            }
        }
        
        //public T LoadScriptObject<T>(ResourceID resID) where T: ScriptableObject
        //{
        //    UnityEngine.Object resourceObj = ResourceManager.instance.GetResourceNoCache(resID);
        //    if(resourceObj == null)
        //    {
        //        return null;
        //    }
            
        //    T asset = resourceObj as T;
            
        //    return asset;
        //}

        private void UnLoadCachedConfigByDataType(string typeStr)
        {
            if (m_ConfigDataLists.ContainsKey(typeStr))
            {
                m_ConfigDataLists.Remove(typeStr);
            }

            if (m_ConfigDataItemss.ContainsKey(typeStr))
            {
                m_ConfigDataItemss.Remove(typeStr);
            }
        }
        public void UnloadCachedConfigs(List<Type> typeList)
        {
            for (int i = 0; i < typeList.Count; i++)
            {
                UnLoadCachedConfigByDataType(typeList[i].ToString());
            }
        }

        public void ReloadCachedConfigs(List<Type> typeList)
        {
            for (int i = 0; i < typeList.Count; i++)
            {
                var typeStr = typeList[i].ToString();
                if (m_ConfigDataLists.ContainsKey(typeStr))
                {
                    continue;
                }

                if (m_ConfigLoadResID.ContainsKey(typeStr))
                {
                    MethodInfo mi = GetType().GetMethod("LoadCSV");
                    if (mi != null)
                    {
                        mi.MakeGenericMethod(typeList[i]).Invoke(this, new System.Object[] {m_ConfigLoadResID[typeStr]});
                    }
                }
            }
        }
    }
}
